package de.koller.reason.render.pipeline;

import de.koller.oceanFramework.math.IProjection;
import de.koller.oceanFramework.util.ColorIntUtil;
import de.koller.oceanFramework.math.RVec3;
import de.koller.reason.render.obj.Polygon;
import de.koller.reason.math.FragmentInterpolation;

public class Rasterizer {

	private final RenderPipeline pipeline;
	
	private final RVec3[] projVec = new RVec3[3];

	private float minX, minY, maxX, maxY;
	private FragmentInterpolation interR;
	private FragmentInterpolation interG;
	private FragmentInterpolation interB;
	
	
	public Rasterizer( RenderPipeline pipeline ) {
		this.pipeline = pipeline;
	}

	public void rasterPolygon( Polygon p, IProjection projection ) {
		for( int i = 0; i < p.verteces.length; i++ )
			projVec[i] = projection.project( p.verteces[i].position, new RVec3() );
		
		interR = new FragmentInterpolation( projVec, new float[] { 
			ColorIntUtil.getRValue( p.verteces[0].color ),
			ColorIntUtil.getRValue( p.verteces[1].color ),
			ColorIntUtil.getRValue( p.verteces[2].color ),
		} );
		
		interG = new FragmentInterpolation( projVec, new float[] { 
			ColorIntUtil.getGValue( p.verteces[0].color ),
			ColorIntUtil.getGValue( p.verteces[1].color ),
			ColorIntUtil.getGValue( p.verteces[2].color ),
		} );
		
		interB = new FragmentInterpolation( projVec, new float[] { 
			ColorIntUtil.getBValue( p.verteces[0].color ),
			ColorIntUtil.getBValue( p.verteces[1].color ),
			ColorIntUtil.getBValue( p.verteces[2].color ),
		} );
		
		calcBounds();
		renderFragments();
	}
	
	private void calcBounds() {
		minX = projVec[0].x;
		maxX = projVec[0].x;
		minY = projVec[0].y;
		maxY = projVec[0].y;
		
		for( int i = 1; i < projVec.length; i++ ) {
			minX = Math.min( minX, projVec[i].x );
			minY = Math.min( minY, projVec[i].y );
			maxX = Math.max( maxX, projVec[i].x );
			maxY = Math.max( maxY, projVec[i].y );
		}
	}
	
	private void renderFragments() {
		for( int x = (int)minX; x < (int)maxX; x++ )
			for( int y = (int)minY; y < (int)maxY; y++ ) {
				Fragment f = new Fragment( x, y, 0xffff0000, 0 );
				if( isWithin( x, y ) ) {
					f.color = ColorIntUtil.getIntColor( 0xff, 
							(int)interR.getValueAt( x, y ),
							(int)interG.getValueAt( x, y ),
							(int)interB.getValueAt( x, y ) );
					pipeline.writeFragment( f );
				}
			}
		
	}
	
	private boolean isWithin( float x, float y ) {
		return ( fAB( x, y ) * fBC( x, y ) >= 0 && fBC( x, y ) * fCA( x, y ) >= 0 );
	}

	private float fAB( float x, float y ) {
		return ( (y - projVec[0].y) * (projVec[1].x - projVec[0].x) ) - 
				( (x - projVec[0].x) * (projVec[1].y - projVec[0].y) );
	}

	private float fBC( float x, float y ) {
		return ( (y - projVec[1].y) * (projVec[2].x - projVec[1].x) ) - 
				( (x - projVec[1].x) * (projVec[2].y - projVec[1].y) );
	}

	private float fCA( float x, float y ) {
		return ( (y - projVec[2].y) * (projVec[0].x - projVec[2].x) ) - 
				( (x - projVec[2].x) * (projVec[0].y - projVec[2].y) );
	}
	
}
